<?php
	/**
	  * @package	MyTools
	  * @name		ClassDefinitos
	  * @brief		Clase para descomponer y mostrar la definicion de una clase especificada
	  * @author		Hermann D. Schimpf <hschimpf@gschimpf.com>
	  * @license	http://opensource.org/licenses/gpl-2.0.php
	  * @date		2010-01-04
	  * @version	0.4.1
  	**/

	final class ClassDefinitor extends MyDebuger implements MyVersions {
		/* version de la clase */
		private static $version			= 0;
		/* subversion de la clase */
		private static $subversion		= 4;
		/* numero de revision de la clase */
		private static $revision		= 1;

		/* creamos un array con las funciones para verificacion de modificadores de clases */
		private $classModifiers		= Array('final' => 'isFinal', 'abstract' => 'isAbstract', 'interface' => 'isInterface');
		/* creamos un array con las funciones para verificacion de modificadores de metodos */
		private $propertyModifiers	= Array('public' => 'isPublic', 'private' => 'isPrivate', 'protected' => 'isProtected', 'static' => 'isStatic');
		/* creamos un array con las funciones para verificacion de modificadores de metodos */
		private $methodModifiers	= Array('public' => 'isPublic', 'private' => 'isPrivate', 'protected' => 'isProtected', 'abstract' => 'isAbstract', 'static' => 'isStatic', 'final' => 'isFinal');

		public function __construct($debug = False) {
			/* creamos el padre */
			parent::__construct($debug, __CLASS__);
		}

		public static function getVersion() {
			/* retornamos la version actual de la clase */
			return 'v' . self::$version . '.' . self::$subversion . '.' . self::$revision;
		}

		public static function showDefinitionOf($classOrInterface = Null, $showProtecteds = False, $showPrivates = False, $debug = False) {
			try {
				try {
					/* creamos una instancia */
					$definition = new ClassDefinitor($debug);
					/* verificamos si ingreso algun valor */
					if ($classOrInterface === Null)
						/* salimos con una excepcion */
						$definition->newException('No se especifico la clase o interfaz a definir');
					/* verificamos si ingreso un string */
					if (gettype($classOrInterface) != 'string')
						/* salimos con una excepcion */
						$definition->newException('El nombre de la clase o interfaz debe ser un string');
					/* verificamos si la clase existe */
					if (!class_exists($classOrInterface) && !interface_exists($classOrInterface))
						/* salimos con una excepcion */
						$definition->newException('La clase ni la interfaz ' . $definition->italic($classOrInterface) . ' existe');
					/* mostramos la cabecera de la clase */
					$definition->showClassHead($classOrInterface);
					/* iniciamos la definicion de metodos de la clase */
					$definition->show($definition->bold('{'));
					/* mostramos la definicion de atributos de la clase */
					$definition->showClassProperties($classOrInterface, $showProtecteds, $showPrivates);
					/* mostramos la definicion de metodos de la clase */
					$definition->showClassMethods($classOrInterface, $showProtecteds, $showPrivates);
					/* finalizamos la definicion de la clase */
					$definition->show($definition->bold('}'));
				} catch (Exception $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$definition->convertToMyException($e);
				}
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$definition->show($e->getMensaje());
			}
		}

		private function showClassHead($classOrInterface) {
			/* mostramos los modificadores de la clase */
			$this->showClassModifiers($classOrInterface);
			/* mostramos el nombre de la clase */
			$this->show($this->classFormat($classOrInterface), False);
			/* mostramos los parientes de la clase */
			$this->showClassParents($classOrInterface);
			/* mostramos las interfaces de la clase */
			$this->showClassInterfaces($classOrInterface);
		}

		private function showClassModifiers($classOrInterface) {
			/* obtenemos y recorremos los modificadores de la clase */
			foreach ($this->getClassModifiers($classOrInterface) AS $modifier)
				/* mostramos el modificador */
				$this->show($this->bold($modifier), False);
			/* verificamos si no es una interface */
			if (!in_array('interface', $this->getClassModifiers($classOrInterface)))
				/* mostramos la palabra class */
				$this->show($this->bold('class'), False);
		}

		private function showClassParents($classOrInterface) {
			/* verificamos si la clase tiene pariente */
			if ($this->getClassParent($classOrInterface) !== False)
				/* mostramos el mensaje extends */
				$this->show($this->bold('extends ') . $this->classFormat($this->getClassParent($classOrInterface)), False);
		}

		private function showClassInterfaces($classOrInterface) {
			/* verificamos si la clase implementa interfaces */
			if (count($this->getClassInterfaces($classOrInterface)) > 0) {
				/* mostramos el mensaje implements */
				$this->show($this->bold('implements '), False);
				/* creamos una bandera para mostrar la coma */
				$first = True;
				/* obtenemos y recorremos las interfaces de la clase */
				foreach ($this->getClassInterfaces($classOrInterface) AS $interface) {
					/* verificamos si ya se mostraron otras interfaces */
					if (!$first)
						/* mostramos una coma */
						$this->show(',', False);
					else
						/* modificamos la bandera */
						$first = False;
					/* mostramos la interface */
					$this->show($this->bold($this->italic($this->blue($interface))), False);
				}
			}
		}

		private function showClassProperties($classOrInterface, $showProtecteds, $showPrivates) {
			/* obtenemos y recorremos los atributos de la clase */
			foreach ($this->getClassProperties($classOrInterface) AS $property) {
				/* verificamos si mostramos el atributo */
				if (
					/* verificamos si el atributo es PUBLIC */
					in_array('public', $this->getPropertyModifiers($classOrInterface, $property)) ||
					/* verificamos si el atributo es PROTECTED y esta habilitada la bandera */
					(in_array('protected', $this->getPropertyModifiers($classOrInterface, $property)) && $showProtecteds === True) ||
					/* verificamos si el atributo es PRIVATE y esta habilitada la bandera */
					(in_array('private', $this->getPropertyModifiers($classOrInterface, $property)) && $showPrivates === True)
				) {
					/* mostramos los modificadores del atributo */
					$this->showPropertyModifiers($classOrInterface, $property);
					/* mostramos el nombre del atributo */
					$this->show($this->attrFormat('$' . $property));
				}
			}
		}

		private function showClassMethods($classOrInterface, $showProtecteds, $showPrivates) {
			/* obtenemos y recorremos los metodos de la clase */
			foreach ($this->getClassMethods($classOrInterface) AS $method) {
				/* verificamos si mostramos el metodo */
				if (
					/* verificamos si el metodo es PUBLIC */
					in_array('public', $this->getMethodModifiers($classOrInterface, $method)) ||
					/* verificamos si el metodo es PROTECTED y esta habilitada la bandera */
					(in_array('protected', $this->getMethodModifiers($classOrInterface, $method)) && $showProtecteds === True) ||
					/* verificamos si el metodo es PRIVATE y esta habilitada la bandera */
					(in_array('private', $this->getMethodModifiers($classOrInterface, $method)) && $showPrivates === True)
				) {
					/* mostramos los modificadores del metodo */
					$this->showMethodModifiers($classOrInterface, $method);
					/* mostramos el nombre del metodo */
					$this->show($this->methodFormat($method) . ' (', False);
					/* mostramos los parametros del metodo */
					$this->showMethodParameters($classOrInterface, $method);
					/* cerramos el parentesis */
					$this->show(')');
				}
			}
		}

		private function showMethodModifiers($classOrInterface, $methodName) {
			/* obtenemos y recorremos los modificadores del metodo */
			foreach ($this->getMethodModifiers($classOrInterface, $methodName) AS $modifier)
				/* mostramos el modificador */
				$this->show($this->bold($modifier), False);
		}

		private function showPropertyModifiers($classOrInterface, $propertyName) {
			/* obtenemos y recorremos los modificadores del atributo */
			foreach ($this->getPropertyModifiers($classOrInterface, $propertyName) AS $modifier)
				/* mostramos el modificador */
				$this->show($this->bold($modifier), False);
		}

		private function showMethodParameters($classOrInterface, $methodName) {
			/* verificamos si tiene parametros */
			if (count($this->getMethodParameters($classOrInterface, $methodName)) > 0) {
				/* creamos una bandera para mostrar la coma */
				$first = True;
				/* obtenemos y recorremos los parametros del metodo */
				foreach ($this->getMethodParameters($classOrInterface, $methodName) AS $parameter) {
					/* verificamos si ya se mostraron otros parametros */
					if (!$first)
						/* mostramos una coma */
						$this->show(',', False);
					else
						/* modificamos la bandera */
						$first = False;
					/* mostramos el parametro */
					$this->show($this->italic($parameter->getName()), False);
				}
			}
		}

		private function getClassModifiers($classOrInterface) {
			/* creamos un array que contendra la salida */
			$modifiers = Array();
			/* creamos una instancia de la clase */
			$classData = new ReflectionClass($classOrInterface);
			/* recorremos los posibles modificadores */
			foreach ($this->classModifiers AS $modifier => $function) {
				/* verificamos si el metodo tiene el modificador */
				if ($classData->$function() === True)
					/* agregamos el modificador a la salida */
					$modifiers[] = $modifier;
			}
			/* retornamos el array con los modificadores */
			return $modifiers;
		}

		private function getClassInterfaces($className) {
			/* creamos un array que contendra la salida */
			$interfaces = Array();
			/* creamos una instancia de la clase */
			$classData = new ReflectionClass($className);
			/* recorremos las interfaces */
			foreach ($classData->getInterfaces() AS $interface)
				/* agregamos la interface a la salida */
				$interfaces[] = $interface->name;
			/* retornamos el array con las interfaces */
			return $interfaces;
		}

		private function getClassParent($className) {
			/* creamos una instancia de la clase */
			$classData = new ReflectionClass($className);
			/* almacenamos el pariente */
			$parent = $classData->getParentClass();
			/* verificamos si tiene valor */
			if ($parent !== False)
				/* retornamos el parent */
				return $parent->name;
			/* retornamos false */
			return False;
		}

		private function getClassProperties($classOrInterfaceName) {
			/* creamos un array que contendra la salida */
			$properties = Array();
			/* creamos una instancia de la clase */
			$classData = new ReflectionClass($classOrInterfaceName);
			/* obtenemos y recorremos los metodos de la clase */
			foreach ($classData->getProperties() AS $property)
				/* agregamos el nombre del metodo a la salida */
				$properties[] = $property->name;
			/* verificamos si tiene padre */
			if ($this->getClassParent($classOrInterfaceName) !== False) {
				/* obtenemos los metodos del padre */
				$parentProperties = $this->getClassProperties($this->getClassParent($classOrInterfaceName));
				/* tomamos solo los metodos de la clase actual */
				$properties = array_diff($properties, $parentProperties);
			}
			/* ordenamos la salida */
			sort($properties);
			/* retornamos los atributos */
			return $properties;
		}

		private function getClassMethods($classOrInterfaceName) {
			/* creamos un array que contendra la salida */
			$methods = Array();
			/* creamos una instancia de la clase */
			$classData = new ReflectionClass($classOrInterfaceName);
			/* obtenemos y recorremos los metodos de la clase */
			foreach ($classData->getMethods() AS $method)
				/* agregamos el nombre del metodo a la salida */
				$methods[] = $method->name;
			/* verificamos si tiene padre */
			if ($this->getClassParent($classOrInterfaceName) !== False) {
				/* obtenemos los metodos del padre */
				$parentMethods = $this->getClassMethods($this->getClassParent($classOrInterfaceName));
				/* tomamos solo los metodos de la clase actual */
				$methods = array_diff($methods, $parentMethods);
				/* verificamos si implementa interfaces */
				if (count($this->getClassInterfaces($classOrInterfaceName)) != 0) {
					/* recorremos las interfaces */
					foreach ($this->getClassInterfaces($classOrInterfaceName) AS $interface) {
						/* recorremos los metodos de la interface */
						foreach ($this->getClassMethods($interface) AS $method) {
							/* verificamos si ya existe el metodo */
							if (!in_array($method, $methods))
								/* agregamos el metodo */
								$methods[] = $method;
						}
					}
				}
			}
			/* ordenamos la salida */
			sort($methods);
			/* retornamos los metodos */
			return $methods;
		}

		private function getPropertyModifiers($classOrInterfaceName, $propertyName) {
			/* creamos un array que contendra la salida */
			$modifiers = Array();
			/* creamos una instancia de la propiedad */
			$propertyData = new ReflectionProperty($classOrInterfaceName, $propertyName);
			/* recorremos los posibles modificadores */
			foreach ($this->propertyModifiers AS $modifier => $function) {
				/* verificamos si el metodo tiene el modificador */
				if ($propertyData->$function() === True)
					/* agregamos el modificador a la salida */
					$modifiers[] = $modifier;
			}
			/* retornamos el array con los modificadores */
			return $modifiers;
		}

		private function getMethodModifiers($classOrInterfaceName, $methodName) {
			/* creamos un array que contendra la salida */
			$modifiers = Array();
			/* creamos una instancia del metodo */
			$methodData = new ReflectionMethod($classOrInterfaceName, $methodName);
			/* recorremos los posibles modificadores */
			foreach ($this->methodModifiers AS $modifier => $function) {
				/* verificamos si el metodo tiene el modificador */
				if ($methodData->$function() === True)
					/* agregamos el modificador a la salida */
					$modifiers[] = $modifier;
			}
			/* retornamos el array con los modificadores */
			return $modifiers;
		}

		private function getMethodParameters($classOrInterfaceName, $methodName) {
			/* creamos un array que contendra los parametros */
			$parameters = Array();
			/* creamos una instancia del metodo */
			$methodData = new ReflectionMethod($classOrInterfaceName, $methodName);
			/* recorremos los parametros */
			foreach ($methodData->getParameters() AS $parameter)
				/* agregamos el parametro al array */
				$parameters[] = $parameter;
			/* retornamos los parametros */
			return $parameters;
		}
	}
?>